<!DOCTYPE html>
<title>Fenced frame disallowed navigations</title>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="/common/utils.js"></script>
<script src="resources/utils.js"></script>
<script src="/fetch/private-network-access/resources/support.sub.js"></script>

<body>

<script>
// Baseline tests:
//   - Embedder can navigate iframe to blob: URL
//   - Embedder can navigate iframe to data: URL
//   - Same-origin embedder can navigate iframe to javascript: URL
//   - Embedder can navigate iframe to http: URL
// Fenced frame tests:
//   - Embedder cannot navigate fenced frame to blob: URL
//   - Embedder cannot navigate fenced frame to data: URL
//   - Same-origin embedder cannot navigate fenced frame to
//     javascript: URL
//   - Embedder cannot navigate fenced frame to http: URL

// Fenced frames are always put in the public IP address space which is the
// least privileged. In case a navigation to a local data: URL or blob: URL
// resource is allowed, they would only be able to fetch things that are *also*
// in the public IP address space. So for the document described by these local
// URLs, we'll set them up to only communicate back to the outer page via
// resources obtained in the public address space.
const kPublicUtils = resolveUrl("resources/utils.js", Server.HTTPS_PUBLIC);

// These are just baseline tests asserting that this test's machinery to load
// blob:, data:, and javascript: URLs work properly in contexts where they are
// expected to.
promise_test(async () => {
  const key = token();
  attachIFrame(`data:text/html, ${createLocalSource(key, kPublicUtils)}`);
  const result = await nextValueFromServer(key);
  assert_equals(result, "LOADED");
}, "iframe data: URL");

promise_test(async () => {
  const key = token();
  const blobURL = URL.createObjectURL(
      new Blob([`${createLocalSource(key, kPublicUtils)}`],
               {type: 'text/html'}));
  attachIFrame(blobURL);
  const result = await nextValueFromServer(key);
  assert_equals(result, "LOADED");
}, "iframe blob: URL");

promise_test(async () => {
  const iframe = attachIFrameContext();
  iframe.src = "javascript:window.jsURLExecuted = true;"
  await iframe.execute(async () => {
    assert_equals(window.jsURLExecuted, true);
  });
}, "iframe javascript: URL");

// The following tests ensure that an embedder cannot navigate a fenced frame
// to:
//   - data: URLs
//   - blob: URLs
//   - javascript: URLs
//   - http: URLs
function getTimeoutPromise(t) {
  return new Promise(resolve =>
      t.step_timeout(() => resolve("NOT LOADED"), 2000));
}

promise_test(async t => {
  const key = token();
  attachFencedFrame(`data:text/html, ${createLocalSource(key, kPublicUtils)}`);
  const loaded_promise = nextValueFromServer(key);
  const result = await Promise.any([loaded_promise, getTimeoutPromise(t)]);
  assert_equals(result, "NOT LOADED");
}, `fenced frame data: URL`);

promise_test(async t => {
  const key = token();
  const blobURL = URL.createObjectURL(
      new Blob([`${createLocalSource(key, kPublicUtils)}`],
               {type: 'text/html'}));
  attachFencedFrame(blobURL);
  const loaded_promise = nextValueFromServer(key);
  const result = await Promise.any([loaded_promise, getTimeoutPromise(t)]);
  assert_equals(result, "NOT LOADED");
}, `fenced frame blob: URL`);

promise_test(async t => {
  const fencedframe = attachFencedFrameContext();
  fencedframe.src = "javascript:window.jsURLExecuted = true;"
  // Just in case the javascript URL executes asynchronously, let's wait for
  // it.
  await getTimeoutPromise(t);
  await fencedframe.execute(async () => {
    assert_equals(window.jsURLExecuted, undefined);
  });
}, `fenced frame javascript: URL`);

promise_test(async t => {
  const key = token();
  let http_url = new URL("resources/embeddee.html",
      get_host_info().HTTP_ORIGIN + location.pathname);
  http_url = generateURL(http_url, [key]);
  assert_equals(http_url.protocol, "http:");
  const fencedframe = attachFencedFrame(http_url);
  const loaded_promise = nextValueFromServer(key);
  const result = await Promise.any([loaded_promise, getTimeoutPromise(t)]);
  assert_equals(result, "NOT LOADED");
}, `fenced frame http: URL`);

</script>

</body>
